/**
 * gnome-gmail-notifier: the gnome gmail notifier.
 * Copyright (C) 2007 Bradley A. Worley.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the
 * 
 *     Free Software Foundation, Inc.
 *     59 Temple Place, Suite 330
 *     Boston, MA 02111-1307 USA
 **/

/*
 * include our application header.
 */
#include <main.h>

/*
 * setup our data columns **EARLY**.
 */
enum {
    ACCOUNT_ENABLED,
    ACCOUNT_NAME,
    ACCOUNT_EMAIL,
    LAST_ELEMENT
};

/*
 * private object definition.
 */
struct _GgnConfigPrivate {
    /* our preferences window. */
    GladeXML* xprefs;
    GtkWindow* prefs;
    
    /* our account edit window. */
    GladeXML* xedit;
    GtkWindow* edit;
    
    /* our accounts list. */
    GtkListStore* list;
    GtkTreeIter iter;
    GtkCellRenderer* cells[LAST_ELEMENT];
    GtkTreeViewColumn* columns[LAST_ELEMENT];
    GtkTreeSelection* sel;

    /* the accounts we are currently modifying. */
    GgnAccount* oldacct;
    GgnAccount* newacct;
    
    /* our preferences. */
    GgnPreferences* settings;
    
    /* our presence. */
    GgnPresence* presence;
};

/*
 * forward function definitions.
 */
static void ggn_config_init (GgnConfig* self);
static void ggn_config_class_init (GgnConfigClass* klass);
static void ggn_config_finalize (GObject* obj);

/*
 * define the gobject type and its basic functions.
 */
G_DEFINE_TYPE (GgnConfig, ggn_config, G_TYPE_OBJECT);

/*
 * ggn_config_update_accounts:
 *
 * Updates the GtkTreeView accounts list by clearing the
 * GtkListStore and repopulating it with the preferences'
 * new values. Brute force, but it's the most comprehensive
 * method.
 *
 * Return value: void.
 */
void ggn_config_update_accounts (GgnConfig* cfg) {
    /* declare some helping variables. */
    GgnAccount* account;
    GSList* accounts;
    gchar* email;
    gint num;

    /* clear the list of accounts. */
    gtk_list_store_clear (cfg->priv->list);

    /* get the accounts listing. */
    accounts = ggn_preferences_get_accounts (cfg->priv->settings);

    /* if the listing is empty, return. */
    if (accounts == NULL)
        return;
    
    /* loop for each available account. */
    for (num = 0; num < g_slist_length (accounts); num++) {
        /* get the email address of the account. */
        email = g_slist_nth_data (accounts, num);

        /* get the account structure. */
        account = ggn_preferences_get_account (cfg->priv->settings, email);

        /* add a new row object. */
        gtk_list_store_append (cfg->priv->list, &cfg->priv->iter);
        
        /* set that object's values. */
        gtk_list_store_set (cfg->priv->list,
                            &cfg->priv->iter,
                            ACCOUNT_ENABLED, account->enab,
                            ACCOUNT_NAME, account->name,
                            ACCOUNT_EMAIL, email,
                            -1);

        /* free the allocated memory. */
        ggn_preferences_account_free (account);
    }

    /* free the accounts listing. */
    g_slist_free (accounts);
}

/*
 * ggn_config_write_soundfile:
 *
 * Saves the sound file selected currently by the
 * file chooser button into the preferences object.
 *
 * Return value: void.
 */
void ggn_config_write_soundfile (GgnConfig* config) {
    /* get a reference to our chooser button. */
    GtkWidget* button = glade_xml_get_widget (config->priv->xprefs,
                                              "btnSoundsChoose");
    
    /* get the newly chosen filename. */
    gchar* file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (button));
    
    /* save the filename in the preferences. */
    ggn_preferences_set_sound_file (config->priv->settings, file);
    
    /* free the filename string. */
    g_free (file);
}

/*
 * ggn_config_key_pressed:
 *
 * Closes the window if escape has been pressed, like a
 * dialog should behave. (Yeah, this is a window.)
 *
 * Return value: signal propagation boolean.
 */
static gboolean ggn_config_key_pressed (GtkWidget* widget,
                                        GdkEventKey* event,
                                        gpointer data) {
    /* gain a reference to our config object. */
    GgnConfig* config = GGN_CONFIG (data);
    
    /* determine what keypress caused this event. */
    if (event->keyval == GGN_CONFIG_KEY_ESCAPE) {
        /* save the currently selected soundfile. */
        ggn_config_write_soundfile (config);
        
        /* yes, so close our window. */
        ggn_config_hide (config);
    }
    
    /* let this event go on. */
    return TRUE;
}

/*
 * ggn_config_edit_show:
 *
 * Updates the fields of the edit window and shows it. The oldacct
 * variable in the GgnConfig object must be set to a valid GgnAccount
 * structure or this will fail.
 *
 * Return value: void.
 */
void ggn_config_edit_show (GgnConfig* cfg) {
    /* set the edit name value. */
    GtkWidget* item = glade_xml_get_widget (cfg->priv->xedit, "txtName");
    gtk_entry_set_text (GTK_ENTRY (item), cfg->priv->oldacct->name);
    
    /* set the edit username value. */
    item = glade_xml_get_widget (cfg->priv->xedit, "txtUser");
    gtk_entry_set_text (GTK_ENTRY (item), cfg->priv->oldacct->user);

    /* set the edit domain value. */
    item = glade_xml_get_widget (cfg->priv->xedit, "txtDomain");
    gtk_entry_set_text (GTK_ENTRY (item), cfg->priv->oldacct->domain);
    
    /* set the edit password value. */
    item = glade_xml_get_widget (cfg->priv->xedit, "txtPass");
    gtk_entry_set_text (GTK_ENTRY (item), cfg->priv->oldacct->pass);
    
    /* open the edit window. */
    gtk_widget_show (GTK_WIDGET (cfg->priv->edit));
}

/*
 * ggn_config_deleted:
 *
 * Called when the user closes the preferences window
 * using the X icon in the window border.
 *
 * Return value: success.
 */
static gboolean ggn_config_deleted (GtkWidget* widget,
                                    GdkEvent* event,
                                    gpointer data) {
    /* get our object. */
    GgnConfig* config = GGN_CONFIG (data);
    
    /* save the currently selected soundfile. */
    ggn_config_write_soundfile (config);
    
    /* hide the window. */
    ggn_config_hide (config);

    /* exit the function. */
    return TRUE;
}

/*
 * ggn_config_accounts_act:
 *
 * Called when the user double-clicks a row of the
 * tree-view.
 *
 * Return value: success.
 */
static gboolean ggn_config_accounts_act (GtkTreeView* widget,
                                         GtkTreePath* path,
                                         GtkTreeViewColumn* column,
                                         gpointer data) {
    /* get a reference to our objects. */
    GgnConfig* config = GGN_CONFIG (data);
    GtkWidget* tree = glade_xml_get_widget (config->priv->xprefs,
                                            "treeAccounts");

    /* declare some helping variables. */
    gchar* email = NULL;
    
    /* get the current selection. */
    config->priv->sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
    
    /* get the iterator. */
    GtkTreeModel* model = GTK_TREE_MODEL (config->priv->list);
    if (!gtk_tree_selection_get_selected (config->priv->sel,
                                          &model,
                                          &config->priv->iter)) {
        /* the user selected nothing? */
        return TRUE;
    }
    
    /* determine the account email address. */
    gtk_tree_model_get (GTK_TREE_MODEL (config->priv->list),
                        &config->priv->iter,
                        ACCOUNT_EMAIL,
                        &email,
                        -1);
    
    /* get the account structure. */
    config->priv->oldacct = ggn_preferences_get_account
                                (config->priv->settings, email);
    
    /* did we find a match? */
    if (config->priv->oldacct != NULL) {
        /* show the edit window. */
        ggn_config_edit_show (config);
    }
    
    /* exit the function. */
    return TRUE;
}

/*
 * ggn_config_accounts_select:
 *
 * Called when the selection of the accounts treeview
 * changes. This is used to disable or enable the Edit
 * button.
 *
 * Return value: void.
 */
static void ggn_config_accounts_select (GtkTreeSelection* sel, gpointer data) {
    /* get the config object. */
    GgnConfig* config = GGN_CONFIG (data);
    
    /* get the edit button. */
    GtkWidget* bedit = glade_xml_get_widget (config->priv->xprefs,
                                             "btnAccountEdit");
    GtkWidget* bdel = glade_xml_get_widget (config->priv->xprefs,
                                            "btnAccountDel");
    
    /* see if anything is selected. */
    GtkTreeModel* model = GTK_TREE_MODEL (config->priv->list);
    if (gtk_tree_selection_get_selected (sel, &model, &config->priv->iter)) {
        /* something is selected. */
        gtk_widget_set_sensitive (bedit, TRUE);
        gtk_widget_set_sensitive (bdel, TRUE);
    }
    else {
        /* nothing is selected. */
        gtk_widget_set_sensitive (bedit, FALSE);
        gtk_widget_set_sensitive (bdel, FALSE);
    }
}

/*
 * ggn_config_accounts_toggle:
 *
 * Called when the user toggles a check button in the
 * GtkTreeView which the accounts list calls home.
 *
 * Return value: void.
 */
static void ggn_config_accounts_toggle (GtkCellRendererToggle* cell,
                                        gchar* path,
                                        gpointer data) {
    /* get a reference to our object. */
    GgnConfig* config = GGN_CONFIG (data);

    /* declare helping variables. */
    gchar* email;
    
    /* get the iterator. */
    GtkTreePath* tree_path = gtk_tree_path_new_from_string (path);
    gtk_tree_model_get_iter (GTK_TREE_MODEL (config->priv->list),
                             &config->priv->iter, tree_path);

    /* determine the account email address. */
    gtk_tree_model_get (GTK_TREE_MODEL (config->priv->list),
                        &config->priv->iter,
                        ACCOUNT_EMAIL,
                        &email,
                        -1);
    
    /* get the account structure. */
    config->priv->oldacct = ggn_preferences_get_account
                                (config->priv->settings, email);
    config->priv->newacct = ggn_preferences_get_account
                                (config->priv->settings, email);

    /* toggle the enabled option. */
    config->priv->newacct->enab = !config->priv->oldacct->enab;

    /* set the new account. */
    ggn_preferences_set_account (config->priv->settings,
                                 config->priv->oldacct,
                                 config->priv->newacct);
    
    /* set the value at the iterator. */
    gtk_list_store_set (config->priv->list,
                        &config->priv->iter,
                        ACCOUNT_ENABLED, config->priv->newacct->enab,
                        -1);
    
    /* free the path. */
    gtk_tree_path_free (tree_path);

    /* free the account and email string. */
    /*ggn_preferences_account_free (config->priv->oldacct);
    ggn_preferences_account_free (config->priv->newacct);*/
    g_free (email);
}

/*
 * ggn_config_accounts_add:
 *
 * Called when the "Add" button in the accounts pane
 * is clicked by the user.
 *
 * Return value: success.
 */
static gboolean ggn_config_accounts_add (GtkWidget* widget,
                                         gpointer data) {
    /* get a reference to our object. */
    GgnConfig* config = GGN_CONFIG (data);
    
    /* make a new account structure. */
    config->priv->oldacct = ggn_preferences_account_new ();
    config->priv->oldacct->name = g_strdup ("New Account");
    config->priv->oldacct->user = g_strdup ("username");
    config->priv->oldacct->domain = g_strdup ("gmail.com");
    config->priv->oldacct->pass = g_strdup ("");
    config->priv->oldacct->enab = FALSE;
    config->priv->oldacct->send = FALSE;

    /* show the account editing window. */
    ggn_config_edit_show (config);
    
    /* update the list. */
    ggn_config_update_accounts (config);
    
    /* exit the function. */
    return TRUE;
}

/*
 * ggn_config_accounts_del:
 *
 * Called when the "Delete" button in the accounts pane
 * is clicked by the user.
 *
 * Return value: success.
 */
static gboolean ggn_config_accounts_del (GtkWidget* widget,
                                         gpointer data) {
    /* get a reference to our object. */
    GgnConfig* config = GGN_CONFIG (data);
    GtkWidget* tree = glade_xml_get_widget (config->priv->xprefs,
                                            "treeAccounts");

    /* declare helping variables. */
    gchar* email = NULL;
    
    /* get the current selection. */
    config->priv->sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
    
    /* get the iterator. */
    GtkTreeModel* model = GTK_TREE_MODEL (config->priv->list);
    if (!gtk_tree_selection_get_selected (config->priv->sel,
                                          &model,
                                          &config->priv->iter)) {
        /* nothing is selected. */
        return TRUE;
    }
    
    /* determine the account name. */
    gtk_tree_model_get (GTK_TREE_MODEL (config->priv->list),
                        &config->priv->iter,
                        ACCOUNT_EMAIL,
                        &email,
                        -1);
    
    /* remove the account in question. */
    config->priv->oldacct = ggn_preferences_get_account
                                (config->priv->settings, email);
    ggn_preferences_remove_account (config->priv->settings,
                                    config->priv->oldacct,
                                    TRUE);
    
    /* update the list. */
    ggn_config_update_accounts (config);
    
    /* exit the function. */
    return TRUE;
}

/*
 * ggn_config_accounts_edit:
 *
 * Called when the "Edit" button in the accounts pane
 * is clicked by the user.
 *
 * Return value: success.
 */
static gboolean ggn_config_accounts_edit (GtkWidget* widget,
                                          gpointer data) {
    /* get a reference to our objects. */
    GgnConfig* config = GGN_CONFIG (data);
    GtkWidget* tree = glade_xml_get_widget (config->priv->xprefs,
                                            "treeAccounts");

    /* declare helping variables. */
    gchar* email = NULL;
    
    /* get the current selection. */
    config->priv->sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
    
    /* get the iterator. */
    GtkTreeModel* model = GTK_TREE_MODEL (config->priv->list);
    if (!gtk_tree_selection_get_selected (config->priv->sel,
                                          &model,
                                          &config->priv->iter)) {
        /* nothing is selected. */
        return TRUE;
    }
    
    /* determine the account name. */
    gtk_tree_model_get (GTK_TREE_MODEL (config->priv->list),
                        &config->priv->iter,
                        ACCOUNT_EMAIL,
                        &email,
                        -1);
    
    /* get the account index by name. */
    config->priv->oldacct = ggn_preferences_get_account
                                (config->priv->settings, email);
    
    /* did we find a match? */
    if (config->priv->oldacct != NULL) {
        /* show the edit window. */
        ggn_config_edit_show (config);
    }
    
    /* exit the function. */
    return TRUE;
}

/*
 * ggn_config_rate_changed:
 *
 * Called when the Update Rate slider changes its value.
 *
 * Return value: success.
 */
static gboolean ggn_config_rate_changed (GtkRange* widget,
                                         gpointer data) {
    /* get a reference to our object. */
    GgnConfig* config = GGN_CONFIG (data);
    
    /* set the value in the settings. */
    ggn_preferences_set_rate (config->priv->settings,
                              (gint) gtk_range_get_value (widget));
    
    /* exit the function. */
    return TRUE;
}

/*
 * ggn_config_rate_format:
 *
 * Called when the Update Rate slider changes its value.
 *
 * Return value: text.
 */
static gchar* ggn_config_rate_format (GtkScale* widget,
                                      gdouble value,
                                      gpointer data) {
    /* see what the value is. */
    if (value == 1) {
        /* print the final string. */
        return g_strdup_printf ("%1.d %s", (gint) value, _("minute"));
    }
    else if (value <= 9) {
        /* print the final string. */
        return g_strdup_printf ("%1.d %s", (gint) value, _("minutes"));
    }
    else {
        /* print the final string. */
        return g_strdup_printf ("%2.d %s", (gint) value, _("minutes"));
    }
}

/*
 * ggn_config_notes_msgs:
 *
 * Called when the message notification checkbox's
 * value changes.
 *
 * Return value: void.
 */
static void ggn_config_notes_msgs (GtkToggleButton* widget, gpointer data) {
    /* get a reference to our object. */
    GgnConfig* config = GGN_CONFIG (data);
    
    /* set the value in the settings. */
    ggn_preferences_set_notify_new (config->priv->settings,
                                    gtk_toggle_button_get_active (widget));
}

/*
 * ggn_config_notes_errs:
 *
 * Called when the message notification checkbox's
 * value changes.
 *
 * Return value: void.
 */
static void ggn_config_notes_errs (GtkToggleButton* widget, gpointer data) {
    /* get a reference to our object. */
    GgnConfig* config = GGN_CONFIG (data);
    
    /* set the value in the settings. */
    ggn_preferences_set_notify_err (config->priv->settings,
                                    gtk_toggle_button_get_active (widget));
}

/*
 * ggn_config_notes_snds:
 *
 * Called when the message notification checkbox's
 * value changes.
 *
 * Return value: void.
 */
static void ggn_config_notes_snds (GtkToggleButton* widget, gpointer data) {
    /* get a reference to our object. */
    GgnConfig* config = GGN_CONFIG (data);
    
    /* set the value in the settings. */
    ggn_preferences_set_sound_enab (config->priv->settings,
                                    gtk_toggle_button_get_active (widget));
}

/*
 * ggn_config_sounds_played:
 *
 * Called when the "Play" button is clicked inside the
 * preferences window.
 *
 * Return value: success.
 */
static gboolean ggn_config_sounds_played (GtkWidget* widget,
                                          gpointer data) {
    /* get our object. */
    GgnConfig* config = GGN_CONFIG (data);
    
    /* get a reference to our chooser button. */
    GtkWidget* button = glade_xml_get_widget (config->priv->xprefs,
                                              "btnSoundsChoose");
    
    /* get the newly chosen filename. */
    gchar* file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (button));
    
    /* play, then free the sound file. */
    ggn_presence_play_sound (config->priv->presence, file);
    g_free (file);
    
    /* exit the function. */
    return TRUE;
}

/*
 * ggn_config_closed:
 *
 * Called when the user closes the preferences window
 * using the "Close" button at the bottom.
 *
 * Return value: success.
 */
static gboolean ggn_config_closed (GtkWidget* widget,
                                   gpointer data) {
    /* get our object. */
    GgnConfig* config = GGN_CONFIG (data);
    
    /* save the currently selected soundfile. */
    ggn_config_write_soundfile (config);
    
    /* hide the window. */
    ggn_config_hide (config);
    
    /* exit the function. */
    return TRUE;
}

/*
 * ggn_edit_deleted:
 *
 * Called when the user closes the edit window
 * using the X icon in the window border.
 *
 * Return value: success.
 */
static gboolean ggn_edit_deleted (GtkWidget* widget,
                                  GdkEvent* event,
                                  gpointer data) {
    /* get our object. */
    GgnConfig* config = GGN_CONFIG (data);
    
    /* hide the window. */
    gtk_widget_hide (GTK_WIDGET (config->priv->edit));

    /* exit the function. */
    return TRUE;
}

/*
 * ggn_edit_closed:
 *
 * Called when the user closes the edit window
 * using the "Cancel" button at the bottom.
 *
 * Return value: success.
 */
static gboolean ggn_edit_closed (GtkWidget* widget,
                                 gpointer data) {
    /* get our object. */
    GgnConfig* config = GGN_CONFIG (data);
    
    /* hide the window. */
    gtk_widget_hide (GTK_WIDGET (config->priv->edit));

    /* exit the function. */
    return TRUE;
}

/*
 * ggn_edit_okied:
 *
 * Called when the user closes the edit window
 * using the "OK" button at the bottom.
 *
 * Return value: success.
 */
static gboolean ggn_edit_okied (GtkWidget* widget,
                                gpointer data) {
    /* get our object. */
    GgnConfig* config = GGN_CONFIG (data);

    /* allocate the new account structure. */
    config->priv->newacct = ggn_preferences_account_new ();
    
    /* get the new name value. */
    GtkWidget* item = glade_xml_get_widget (config->priv->xedit, "txtName");
    config->priv->newacct->name = (gchar*) gtk_entry_get_text (GTK_ENTRY (item));
    
    /* get the new username value. */
    item = glade_xml_get_widget (config->priv->xedit, "txtUser");
    config->priv->newacct->user = (gchar*) gtk_entry_get_text (GTK_ENTRY (item));

    /* get the new domain value. */
    item = glade_xml_get_widget (config->priv->xedit, "txtDomain");
    config->priv->newacct->domain = (gchar*) gtk_entry_get_text (GTK_ENTRY (item));
    
    /* get the new password value. */
    item = glade_xml_get_widget (config->priv->xedit, "txtPass");
    config->priv->newacct->pass = (gchar*) gtk_entry_get_text (GTK_ENTRY (item));

    /* get the new enabled value. */
    config->priv->newacct->enab = config->priv->oldacct->enab;

    /* update the account. */
    ggn_preferences_set_account (config->priv->settings,
                                 config->priv->oldacct,
                                 config->priv->newacct);
    
    /* update the tree view. */
    ggn_config_update_accounts (config);
    
    /* hide the window. */
    gtk_widget_hide (GTK_WIDGET (config->priv->edit));

    /* exit the function. */
    return TRUE;
}

/*
 * ggn_config_init:
 *
 * This function is used by the gobject library to
 * generate a new instance of our object.
 */
static void ggn_config_init (GgnConfig* self) {
    /* set up the private data structure. */
    self->priv = g_new0 (GgnConfigPrivate, 1);

    /* set the initial accounts to NULL. */
    self->priv->oldacct = NULL;
    self->priv->newacct = NULL;
    
    /* locate the glade xml file. */
    gchar* filename = ggn_glade_file (GGN_GLADE_PREFS);
    GtkWidget* item;
    
    /* make the preferences window. */
    self->priv->xprefs = glade_xml_new (filename, NULL, NULL);
    self->priv->prefs = (GtkWindow*) glade_xml_get_widget (self->priv->xprefs,
                                                           "GgnPrefsWindow");
    
    /* link the closed callback to the window. */
    g_signal_connect (G_OBJECT (self->priv->prefs), 
                      "delete_event",
                      G_CALLBACK (ggn_config_deleted),
                      self);
    
    /* link the key release callback to the window. */
    g_signal_connect (G_OBJECT (self->priv->prefs),
                      "key-press-event",
                      G_CALLBACK (ggn_config_key_pressed),
                      self);
    
    /* setup the accounts list. */
    self->priv->list = gtk_list_store_new (LAST_ELEMENT,
                                           G_TYPE_BOOLEAN,
                                           G_TYPE_STRING,
                                           G_TYPE_STRING);
    
    /* set the model for the tree view. */
    item = glade_xml_get_widget (self->priv->xprefs, "treeAccounts");
    gtk_tree_view_set_model (GTK_TREE_VIEW (item),
                             GTK_TREE_MODEL (self->priv->list));
    
    /* setup the account cell renderers. */
    self->priv->cells[ACCOUNT_ENABLED] = gtk_cell_renderer_toggle_new ();
    self->priv->cells[ACCOUNT_NAME] = gtk_cell_renderer_text_new ();
    
    /* set the activatable property of the cell renderer. */
    g_object_set (G_OBJECT (self->priv->cells[ACCOUNT_ENABLED]),
                  "activatable", TRUE, NULL);
    
    /* link the toggled event to our first cell. */
    g_signal_connect (G_OBJECT (self->priv->cells[ACCOUNT_ENABLED]),
                      "toggled",
                      G_CALLBACK (ggn_config_accounts_toggle),
                      self);
    
    /* setup the account columns. */
    self->priv->columns[ACCOUNT_ENABLED] =
        gtk_tree_view_column_new_with_attributes (_("Enabled"),
                                                  self->priv->cells[ACCOUNT_ENABLED],
                                                  "active", ACCOUNT_ENABLED, NULL);
    self->priv->columns[ACCOUNT_NAME] =
        gtk_tree_view_column_new_with_attributes (_("Name"),
                                                  self->priv->cells[ACCOUNT_NAME],
                                                  "text", ACCOUNT_NAME, NULL);
    
    /* append the columns to our accounts tree view widget. */
    gtk_tree_view_append_column (GTK_TREE_VIEW (item),
                                 self->priv->columns[ACCOUNT_ENABLED]);
    gtk_tree_view_append_column (GTK_TREE_VIEW (item),
                                 self->priv->columns[ACCOUNT_NAME]);
    
    
    /* setup the selection callback. */
    self->priv->sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (item));
    
    /* link the changed callback to the selection. */
    g_signal_connect (G_OBJECT (self->priv->sel),
                      "changed",
                      G_CALLBACK (ggn_config_accounts_select),
                      self);
    
    /* link the row_activated callback to the tree view. */
    g_signal_connect (G_OBJECT (item),
                      "row-activated",
                      G_CALLBACK (ggn_config_accounts_act),
                      self);
    
    /* get a reference to the add button. */
    item = glade_xml_get_widget (self->priv->xprefs, "btnAccountAdd");
    
    /* link the clicked callback to the button. */
    g_signal_connect (G_OBJECT (item),
                      "clicked",
                      G_CALLBACK (ggn_config_accounts_add),
                      self);
    
    /* get a reference to the delete button. */
    item = glade_xml_get_widget (self->priv->xprefs, "btnAccountDel");
    
    /* link the clicked callback to the button. */
    g_signal_connect (G_OBJECT (item),
                      "clicked",
                      G_CALLBACK (ggn_config_accounts_del),
                      self);
    
    /* get a reference to the edit button. */
    item = glade_xml_get_widget (self->priv->xprefs, "btnAccountEdit");
    
    /* link the clicked callback to the button. */
    g_signal_connect (G_OBJECT (item),
                      "clicked",
                      G_CALLBACK (ggn_config_accounts_edit),
                      self);
    
    /* get a reference to the slider. */
    item = glade_xml_get_widget (self->priv->xprefs, "slideUpdates");
    
    /* link the value_changed callback to the slider. */
    g_signal_connect (G_OBJECT (item),
                      "value_changed",
                      G_CALLBACK (ggn_config_rate_changed),
                      self);
    
    /* link the format_value callback to the slider. */
    g_signal_connect (G_OBJECT (item),
                      "format_value",
                      G_CALLBACK (ggn_config_rate_format),
                      self);
    
    /* get a reference to the message checkbox. */
    item = glade_xml_get_widget (self->priv->xprefs, "chkNotesMsgs");
    
    /* link the clicked callback to the button. */
    g_signal_connect (G_OBJECT (item),
                      "toggled",
                      G_CALLBACK (ggn_config_notes_msgs),
                      self);
    
    /* get a reference to the message checkbox. */
    item = glade_xml_get_widget (self->priv->xprefs, "chkNotesErrs");
    
    /* link the clicked callback to the button. */
    g_signal_connect (G_OBJECT (item),
                      "toggled",
                      G_CALLBACK (ggn_config_notes_errs),
                      self);
    
    /* get a reference to the message checkbox. */
    item = glade_xml_get_widget (self->priv->xprefs, "chkSounds");
    
    /* link the clicked callback to the button. */
    g_signal_connect (G_OBJECT (item),
                      "toggled",
                      G_CALLBACK (ggn_config_notes_snds),
                      self);
    
    /* get a reference to the play button. */
    item = glade_xml_get_widget (self->priv->xprefs, "btnSoundsPlay");
    
    /* link the clicked callback to the button. */
    g_signal_connect (G_OBJECT (item),
                      "clicked",
                      G_CALLBACK (ggn_config_sounds_played),
                      self);
    
    /* get a reference to the open button. */
    item = glade_xml_get_widget (self->priv->xprefs, "btnSoundsChoose");
    
    /* create a filetype filter for our file chooser. */
    GtkFileFilter* filter = gtk_file_filter_new ();
    gtk_file_filter_set_name (filter, _("Wave audio files"));
    gtk_file_filter_add_pattern (filter, "*.wav");
    gtk_file_filter_add_mime_type (filter, "audio/x-wav");

    /* apply our filter to the file chooser. */
    gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (item), filter);
    
    /* get a reference to the close button. */
    item = glade_xml_get_widget (self->priv->xprefs, "btnClose");
    
    /* link the clicked callback to the button. */
    g_signal_connect (G_OBJECT (item),
                      "clicked",
                      G_CALLBACK (ggn_config_closed),
                      self);
    
    /* free the file string. */
    g_free (filename);
    
    /* locate the glade xml file. */
    filename = ggn_glade_file (GGN_GLADE_EDIT);
    
    /* make the edit window. */
    self->priv->xedit = glade_xml_new (filename, NULL, NULL);
    self->priv->edit = (GtkWindow*) glade_xml_get_widget (self->priv->xedit,
                                                           "GgnEditWindow");
    
    /* set some values of the edit window. */
    gtk_window_set_modal (self->priv->edit, TRUE);
    gtk_window_set_transient_for (self->priv->edit, self->priv->prefs);
    
    /* link the closed callback to the window. */
    g_signal_connect (G_OBJECT (self->priv->edit), 
                      "delete_event",
                      G_CALLBACK (ggn_edit_deleted),
                      self);
    
    /* get a reference to the close button. */
    item = glade_xml_get_widget (self->priv->xedit, "btnCancel");
    
    /* link the clicked callback to the button. */
    g_signal_connect (G_OBJECT (item),
                      "clicked",
                      G_CALLBACK (ggn_edit_closed),
                      self);
    
    /* get a reference to the close button. */
    item = glade_xml_get_widget (self->priv->xedit, "btnOK");
    
    /* link the clicked callback to the button. */
    g_signal_connect (G_OBJECT (item),
                      "clicked",
                      G_CALLBACK (ggn_edit_okied),
                      self);
    
    /* free the file string. */
    g_free (filename);
}

/*
 * ggn_config_class_init:
 *
 * This function is used by the gobject library to
 * generate a new class object of our object.
 */
static void ggn_config_class_init (GgnConfigClass* klass) {
    /* setup a gobject class. */
    GObjectClass* gobj_class = G_OBJECT_CLASS (klass);
    
    /* set the locations of our destruction function. */
    gobj_class->finalize = ggn_config_finalize;
    
    /* setup the default signal handlers. */
}

/*
 * ggn_config_finalize:
 *
 * This function is used by the gobject library to cleanly finish
 * the destruction process started by the dispose function.
 */
static void ggn_config_finalize (GObject* obj) {
    /* make a reference to ourself. */
    GgnConfig* self = GGN_CONFIG (obj);
    
    /* free the glade xml and windows. */
    g_object_unref (G_OBJECT (self->priv->xprefs));
    g_object_unref (G_OBJECT (self->priv->xedit));
    gtk_widget_destroy (GTK_WIDGET (self->priv->prefs));
    gtk_widget_destroy (GTK_WIDGET (self->priv->edit));
    
    /* free the list store. */
    gtk_list_store_clear (self->priv->list);
    g_object_unref (G_OBJECT (self->priv->list));
    
    /* destroy the private object. */
    g_free (self->priv);
    self->priv = NULL;
    
    /* chain up to the parent class. */
    G_OBJECT_CLASS (ggn_config_parent_class)->finalize (obj);
}

/*
 * ggn_config_new:
 *
 * Creates a new GgnPresence object for use in the application.
 * It maintains a presence on the panel, with sound effects,
 * and with libnotify notifications.
 *
 * Return value: the new about dialog object.
 */
GgnConfig* ggn_config_new (void) {
    /* make a newly created gobject. */
    GgnConfig* cfg = g_object_new (GGN_TYPE_CONFIG, NULL);
    
    /* return the new object. */
    return cfg;
}

/*
 * ggn_config_free:
 *
 * Frees the given preferences window by
 * decreasing its reference count.
 *
 * Return value: void.
 */
void ggn_config_free (GgnConfig* cfg) {
    /* unreference the object. */
    while (G_IS_OBJECT (cfg)) {
        /* unreference this object. */
        g_object_unref (G_OBJECT (cfg));
    }
}

/*
 * ggn_config_show:
 *
 * Makes the provided preferences window visible.
 *
 * Return value: void.
 */
void ggn_config_show (GgnConfig* cfg) {
    /* see if we have a prefs object. */
    if (cfg->priv->settings != NULL) {
        /* declare a widget. */
        GtkWidget* item;
        
        /* update the accounts list. */
        ggn_config_update_accounts (cfg);
        
        /* set the value of the rate slider. */
        item = glade_xml_get_widget (cfg->priv->xprefs, "slideUpdates");
        gtk_range_set_value (GTK_RANGE (item),
            (gdouble) ggn_preferences_get_rate (cfg->priv->settings));
        
        /* set the value of the messages checkbox. */
        item = glade_xml_get_widget (cfg->priv->xprefs, "chkNotesMsgs");
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item),
            ggn_preferences_get_notify_new (cfg->priv->settings));
        
        /* set the value of the errors checkbox. */
        item = glade_xml_get_widget (cfg->priv->xprefs, "chkNotesErrs");
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item),
            ggn_preferences_get_notify_err (cfg->priv->settings));
        
        /* set the value of the sounds checkbox. */
        item = glade_xml_get_widget (cfg->priv->xprefs, "chkSounds");
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item),
            ggn_preferences_get_sound_enab (cfg->priv->settings));
        
        /* get the sound filename. */
        gchar* sound = ggn_preferences_get_sound_file (cfg->priv->settings);
        
        /* set the value of the sound file chooser button. */
        item = glade_xml_get_widget (cfg->priv->xprefs, "btnSoundsChoose");
        gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (item), sound);
        
        /* free the sound file string. */
        g_free (sound);
        
        /* no first run. */
        ggn_preferences_set_firstrun (cfg->priv->settings, FALSE);
    }
    
    /* show the widget. */
    gtk_widget_show (GTK_WIDGET (cfg->priv->prefs));
}

/*
 * ggn_config_show:
 *
 * Makes the provided preferences window visible.
 *
 * Return value: void.
 */
void ggn_config_hide (GgnConfig* cfg) {
    /* hide the widget. */
    gtk_widget_hide (GTK_WIDGET (cfg->priv->prefs));
}

/*
 * ggn_config_set_prefs:
 *
 * Gives the preferences window a handle on a preferences file.
 *
 * Return value: void.
 */
void ggn_config_set_prefs (GgnConfig* cfg, GgnPreferences* prefs) {
    /* set the object. */
    cfg->priv->settings = prefs;
}

/*
 * ggn_config_set_presence:
 *
 * Gives the preferences window a handle on a presence object.
 *
 * Return value: void.
 */
void ggn_config_set_presence (GgnConfig* cfg, GgnPresence* pres) {
    /* set the object. */
    cfg->priv->presence = pres;
}

